﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace WorldOfMUDra
{
    /// <summary>
    /// Areas are essentially what the world is made of. They hold players and contain descriptions of their contents 
    /// loaded from xml files. Using the LinkWith() method, you can easily make a network of areas, thereby creating 
    /// a world.
    /// </summary>
    public class Area
    {
        string name;
        string id;
        string description; 
        string virginmessage;

        Dictionary<string, Area> Portals = new Dictionary<string, Area>();
        List<Player> Occupants = new List<Player>();
        Dictionary<string,IItem> items = new Dictionary<string,IItem>();
        List<AreaEvent> OnFirstVisitEvents = new List<AreaEvent>();

        /// <summary>
        /// Constructor for Area class that takes in the name of file containing data about the area.
        /// </summary>
        /// <param name="filename">Name of file to parse for data. XML file extensions will be detected and parsed differently than text files. XML is prefered.</param>
        public Area(string filename)
        {
            Console.WriteLine("Loading "+filename+" as an Area...");
            if (filename.Split('.')[1].ToLower() == "xml")
            {
                Console.WriteLine("XML file extension detected. Will attempt to parse as XML document...");
                XmlTextReader reader = new XmlTextReader(filename);
                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            switch (reader.Name.ToLower())
                            {
                                case "description":
                                    this.description = Globals.RemoveExtraSpaces(reader.ReadString().Trim());
                                    //Console.WriteLine("Added description '" + this.description + "'");
                                    break;
                                case "name":
                                    this.name = reader.ReadString().Trim();
                                    Console.WriteLine("Added name '" + this.name + "'");
                                    break;
                                case "id":
                                    this.id = reader.ReadString().Trim();
                                    Console.WriteLine("Added id '" + this.id + "'");
                                    break;
                                case "virginmessage":
                                    this.virginmessage = Globals.RemoveExtraSpaces(reader.ReadString().Trim());
                                    //Console.WriteLine("Added V.M. '" + this.virginmessage + "'");
                                    break;
                            }
                            break;
                    }
                }
            }
            else
            {
                try
                {
                    string[] lines = File.ReadAllLines(filename);
                    if (lines.Length >= 4)
                    {
                        name = lines[0].Trim();
                        id = lines[1].Trim();
                        virginmessage = lines[2].Trim();
                        string desc = "";
                        for (int i = 3; i < lines.Length; i++)
                        {
                            desc += (lines[i].TrimEnd() + ' ');
                        }
                        description = desc.Trim();
                    }
                    else
                    {
                        throw new Exception();
                    }
                }
                catch (Exception)
                {
                    Console.WriteLine("Issues loading area config file " + filename);
                }
            }
            Console.WriteLine(filename + " loaded.");
            Console.WriteLine();
            World.Areas.Add(this.id, this);
        }

        /// <summary>
        /// Lazy constructor for Area that takes only a name and a description.
        /// </summary>
        /// <param name="name">The name of the area.</param>
        /// <param name="description">The description of the area.</param>
        public Area(string name, string description)
        {
            this.name = name;
            this.description = description;
            World.Areas.Add(this.id, this);
        }

        public string Name
        {
            get { return (name + " (" + id + ")"); }
        }

        public string ID
        {
            get { return id; }
        }

        public string Description
        {
            get 
            {
                string places = "";
                string FullDesc;
                foreach (Area place in Portals.Values)
                {
                    places += " " + place.Name+",";
                }
                
                FullDesc = this.description;
                if (!places.Equals(""))
                {
                    FullDesc += " You see paths to" + places.TrimEnd(',') + ".";
                }
                return FullDesc; 
            }
            set { description = value; }
        }

        public string VirginMessage
        {
            get 
            {
                if (this.virginmessage != "[none]")
                {
                    return virginmessage;
                }
                else
                {
                    return null;
                }
            }
        }

        public void LinkWith(Area area)
        {
            if (!Portals.ContainsKey(area.ID))
            {
                Portals.Add(area.ID, area);
            }
        }

        public bool LeadsTo(string portalID)
        {
            return this.Portals.ContainsKey(portalID.Trim());
        }

        public void RemovePlayer(Player p)
        {
            this.Occupants.Remove(p);
        }

        public void OnFirstVisit(Player visitor)
        {
            foreach (AreaEvent eve in this.OnFirstVisitEvents)
            {
                eve(visitor);
            }
        }

        public void AddOnFirstVisitEvent(AreaEvent Event)
        {
            this.OnFirstVisitEvents.Add(Event);
        }

        public void MoveHere(Player p)
        {
            if (p.CurrentArea() != null) p.CurrentArea().RemovePlayer(p);
            p.MoveTo(this);
            this.Occupants.Add(p);
            if (!p.HasVisited(this))
            {
                p.SendWorldMessage(this.VirginMessage);
                this.OnFirstVisit(p);
            }
            p.AddVisitedArea(this.id);
        }

        public void ChatToOccupants(string username, string msg)
        {
            foreach (Player occupant in this.Occupants)
            {
                occupant.SendChatMessage(username, msg);
            }
        }

        public void WorldMessageToOccupants(string msg)
        {
            foreach (Player occupant in this.Occupants)
            {
                occupant.SendWorldMessage(msg);
            }
        }

        public Area Places(string IDOf)
        {
            return Portals[IDOf];
        }

        public bool ContainsItem(string itemID)
        {
            return this.items.ContainsKey(itemID);
        }

        public void AddItem(IItem item)
        {
            this.items.Add(item.ID, item);
        }

        public IItem GetItem(string itemID)
        {
            if (this.items.ContainsKey(itemID))
            {
                return items[itemID];
            }
            else
            {
                return null;
            }
        }

        public string ItemList
        {
            get
            {
                string r = "";
                foreach (IItem item in this.items.Values)
                {
                    r += item.Name + ", ";
                }
                return r.TrimEnd(' ', ',');
            }
        }
    }
}
